Optimize Android emulator initialization by batching reboots#5280
Optimize Android emulator initialization by batching reboots#5280jardondiego wants to merge 10 commits into
Conversation
This change reduces the number of reboots during Android device setup by: - Adding a wait_for_reboot parameter to adb.write_data_to_file. - Tracking reboot status in initialize_device to skip the final reboot if one already occurred. - Disabling reboots when setting sanitizer options since the app restart is sufficient. These optimizations improve bot startup efficiency and overall fuzzing throughput.
… tracking and short-circuit issues
There was a problem hiding this comment.
Nit:
Add typing hints and update the string doc for this method so that other devs reading this know what the bool means
| return True | ||
|
|
||
|
|
||
| def get_debug_props_and_values(): |
There was a problem hiding this comment.
Nit x2:
Add typing hints and update the string doc for this method so that other devs reading this know what the bool means
| sanitizer_options, sanitizer_options_file_path, wait_for_reboot=False) | ||
|
|
||
|
|
||
| def setup_asan_if_needed(): |
There was a problem hiding this comment.
Nit x3:
Add typing hints and update the string doc for this method so that other devs reading this know what the bool means
fernandofloresg
left a comment
There was a problem hiding this comment.
lgtm, would be nice to add those docstrings
| return True | ||
|
|
||
|
|
||
| def get_debug_props_and_values(): |
Adds `-> bool` return type hints and explanatory docstrings to: - `configure_system_build_properties` - `setup_asan_if_needed` These updates clarify that the returned boolean indicates whether a device reboot occurred during the setup step.
letitz
left a comment
There was a problem hiding this comment.
I'm a bit confused about the main logic change in initialize_device(), see comment below.
|
|
||
|
|
||
| def write_data_to_file(contents, file_path): | ||
| def write_data_to_file(contents, file_path, wait_for_reboot=True): |
There was a problem hiding this comment.
We should document the new argument at least. While we're at it, we can document the whole function: args, return value, any exceptions it raises.
| current_md5 = adb.get_file_checksum(BUILD_PROP_PATH) | ||
| persistent_cache.set_value(constants.BUILD_PROP_MD5_KEY, current_md5) | ||
|
|
||
| return True |
There was a problem hiding this comment.
If I'm reading this right, we only rebooted on line 239 if the android version was as least M?
|
|
||
| # General device configuration settings. | ||
| configure_system_build_properties() | ||
| needs_full_reboot = configure_system_build_properties() |
There was a problem hiding this comment.
The docstring above states that this function returns whether the device was already rebooted, not whether it needed to be rebooted. Which is it?
| # Reboot device as above steps would need it and also it brings device in a | ||
| # good state. | ||
| reboot() | ||
| if needs_full_reboot or not asan_reboot_done: |
There was a problem hiding this comment.
I'm having a hard time understanding the logic here, regardless of whether this boolean really means needs_full_reboot or if it means already_rebooted, per the above comment.
In the needs_full_reboot case: if we needed to reboot after configuring the device, and the asan setup step already rebooted it, then why do we reboot once more here? In other words, why is this not:
| if needs_full_reboot or not asan_reboot_done: | |
| if needs_full_reboot and not asan_reboot_done: |
In the already_rebooted case: why do avoid rebooting if already_rebooted is true? I would expect instead:
| if needs_full_reboot or not asan_reboot_done: | |
| if not already_rebooted and not asan_reboot_done: |
| return | ||
|
|
||
| adb.write_data_to_file(sanitizer_options, sanitizer_options_file_path) | ||
| # Skip reboot as the app will pick up the options file on restart. |
There was a problem hiding this comment.
Just to make sure I understand, this means that the next time the app is started, it will read from the options file?
|
|
||
| def setUp(self): | ||
| # Mock environment to bypass the engine fuzzer check | ||
| mock.patch( |
| self.mock_setup_asan.return_value = False | ||
|
|
||
| device.initialize_device() | ||
| self.mock_reboot.assert_called_once() |
There was a problem hiding this comment.
We test False/True, True/False and False/False. What about True/True? This goes back to my comment about or not vs and not.
| mock.patch.stopall() | ||
|
|
||
| def test_reboot_if_props_changed(self): | ||
| """Test that it reboots if build.prop changed, even if ASan didn't run.""" |
There was a problem hiding this comment.
| """Test that it reboots if build.prop changed, even if ASan didn't run.""" | |
| """Test that `initialize_device()` reboots the device if | |
| `configure_system_build_properties()` did, and the ASan setup script did not. |
| self.mock_reboot.assert_called_once() | ||
|
|
||
| def test_no_reboot_if_asan_ran(self): | ||
| """Test that the final reboot is skipped if ASan setup performed a shell restart.""" |
There was a problem hiding this comment.
| """Test that the final reboot is skipped if ASan setup performed a shell restart.""" | |
| """Test that `initialize_device()` skips calling `reboot()` if | |
| `configure_system_build_properties()` did not cause a reboot but the ASan | |
| setup script did. | |
| """ |
| self.mock_reboot.assert_not_called() | ||
|
|
||
| def test_reboot_if_clean_slate_needed(self): | ||
| """Test that it still reboots to ensure a clean state if no other steps restarted it.""" |
There was a problem hiding this comment.
| """Test that it still reboots to ensure a clean state if no other steps restarted it.""" | |
| """Test that `initialize_device()` calls `reboot()` if neither | |
| `configure_system_build_properties()` nor the ASan setup script did. | |
| """ |
This change reduces the number of reboots during Android device setup by:
These optimizations improve bot startup efficiency and overall fuzzing throughput.